#ifndef APPLICATION_H
#define APPLICATION_H

#include "Core/base.h"
#include "Core/window.h"
#include "Core/layerStack.h"
#include "Events/event.h"
#include "Events/applicationEvent.h"

#include "Core/timestep.h"

#include "ImGui/imGuiLayer.h"

#include <mutex>

namespace CC
{

	struct ApplicationCommandLineArgs
	{
		int Count = 0;
		char **Args = nullptr;

		const char *operator[](int index) const
		{
			return Args[index];
		}
	};

	struct ApplicationSpecification
	{
		std::string Name = "CC Application";
		std::string WorkingDirectory;
		ApplicationCommandLineArgs CommandLineArgs;
		uint32_t Width;
		uint32_t Height;
        bool HasImGui = false;
        bool Floating = false;//top window
        bool HasTitle = true;
	};

    typedef struct _ThreadInfo
    {
        std::function<void(void* param)> func;
        void* param;

        _ThreadInfo(std::function<void(void* param)> f, void* p) : func(f), param(p) {

        }
    }ThreadInfo;

	class CC_EXPORT Application
	{
	public:
		Application(const ApplicationSpecification &specification);
		virtual ~Application();

		void OnEvent(Event &e);

		void PushLayer(Layer *layer);
		void PushOverlay(Layer *layer);

        void PopLayer(Layer* layer);
        void PopOverlay(Layer* layer);

        LayerStack& GetLayerStack() { return m_LayerStack; }

		Window &GetWindow() { return *m_Window; }
		static Application &Get() { return *s_Instance; }
		const ApplicationSpecification &GetSpecification() const { return m_Specification; }

        void SetWindowTitle(std::string name);

		void SubmitToMainThread(ThreadInfo& info);

		void Close();
		void Run();

        int GetFpsValueInt() { return m_fpsCounter; }

	private:
		
		void OnWindowClose(WindowCloseEvent &e);
		void OnWindowResize(WindowResizeEvent &e);

		void ExecuteMainThreadQueue();

	private:
		Window *m_Window;
		ApplicationSpecification m_Specification;

		ImGuiLayer* m_ImGuiLayer;
		bool m_Running = true;
		bool m_Minimized = false;
		static Application *s_Instance;
		LayerStack m_LayerStack;
		float m_LastFrameTime = 0.0f;
        float m_fpsTime = 0.0f;
        int m_fpsCounter = 0;

		std::vector<ThreadInfo> m_MainThreadQueue;
		std::mutex m_MainThreadQueueMutex;
	};

	// To be defined in CLIENT
	Application *CreateApplication(ApplicationCommandLineArgs args);
}

#endif